<?php

/**
 * -----------| 所有Xml格式数据实体类的父类 |-----------
 * @category Bb
 * @package core.model
 * @author skygreen2001 <skygreen2001@gmail.com>
 */
class XmlObject extends BBObject implements ArrayAccess
{
    /**
    * 库的唯一标识:库的名称
    * @var string
    */
    protected $id;
    /**
     * @var int 记录创建的时间timestamp
     */
    protected $commitTime;
    /**
     * @var int 记录最后更新的时间，当表中无该字段时，一般用commitTime记录最后更新的时间。
     */
    protected $updateTime;

    private static $name_id_property = "id";

    //<editor-fold defaultstate="collapsed" desc="默认列Setter和Getter">
    /**
     * 设置唯一标识
     * @param mixed $id
     */
    public function setId($id)
    {
        $this->id = $id;
    }

    /**
     * 获取唯一标识
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * 设置数据创建的时间
     * @param mixed $commitTime
     */
    public function setCommitTime($commitTime)
    {
        $this->commitTime = $commitTime;
    }

    /**
     * 获取数据创建的时间
     * @return mixed
     */
    public function getCommitTime()
    {
        return $this->commitTime;
    }

    /**
     * 设置数据最后更新的时间
     * @param mixed $updateTime
     */
    public function setUpdateTime($updateTime)
    {
        $this->updateTime = $updateTime;
    }

    /**
     * 获取数据最后更新的时间
     * @return mixed
     */
    public function getUpdateTime()
    {
        return $this->updateTime;
    }
    //</editor-fold>


    //<editor-fold defaultstate="collapsed" desc="魔术方法">
    /**
     * 可设定对象未定义的成员变量[但不建议这样做]
     * 类定义变量访问权限设定需要是pulbic
     * @param mixed $property 属性名
     * @return mixed 属性值
     */
    public function __get($property)
    {
        if (method_exists($this, "get" . ucfirst($property))) {
            $methodname = "get" . ucfirst($property);
            return $this->{$methodname}();
        } else {
            if (!property_exists($this, $property)) {
                return @$this->{$property};
            }
        }
    }

    /**
     * 可设定对象未定义的成员变量[但不建议这样做]
     * 类定义变量访问权限设定需要是pulbic
     * @param mixed $property 属性名
     * @param mixed $value 属性值
     */
    public function __set($property, $value)
    {
        if (method_exists($this, "set" . ucfirst($property))) {
            $methodname = "set" . ucfirst($property);
            $this->{$methodname}($value);
        } else {
            if (!property_exists($this, $property)) {
                $this->{$property} = $value;
            }
        }
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="定义数组进入对象方式">
    /**
     * Whether a offset exists
     *
     * @param mixed $key — An offset to check for.
     * @access public
     * @return bool
     * @abstracting ArrayAccess
     */
    #[\ReturnTypeWillChange]
    public function offsetExists($key)
    {
        $method = "get" . ucfirst($key);
        return method_exists($this, $method);
    }

    /**
     * Offset to retrieve
     *
     * @param mixed $key — The offset to retrieve.
     * @access public
     * @return mixed — Can return all value types.
     * @abstracting ArrayAccess
     */
    public function offsetGet($key)
    {
        $method = "get" . ucfirst($key);
        return $this->$method();
    }

    /**
     * Offset to set
     * @param mixed $key — The offset to assign the value to.
     * @param mixed $value — The value to set.
     * @access public
     * @return void
     * @abstracting ArrayAccess
     */
    #[\ReturnTypeWillChange]
    public function offsetSet($key, $value)
    {
        $method = "set" . ucfirst($key);
        $this->$method($value);
//        $this->$key = $value;
    }

    /**
     * Offset to unset
     * @param mixed $key — The offset to unset.
     * @access public
     * @return void
     * @abstracting ArrayAccess
     */
    #[\ReturnTypeWillChange]
    public function offsetUnset($key)
    {
        unset($this->$key);
    }
    //</editor-fold>

    /**
     * Xml格式存储的文件路径地址
     */
    public static function address()
    {
        return Gc::$nav_root_path . basename(__FILE__, ConfigF::SUFFIX_FILE_PHP) . ConfigF::SUFFIX_FILE_XML;
    }

    /**
     * 获取所有Xml对象的信息
     *
     * @param string $xmlObject_classname 具体的Xml对象类名
     * @param string $filter 查询条件，在where后的条件
     *
     * 示例如下:
     *
     *      0. "id=1,name='sky'"
     *      1. array("id=1","name='sky'")
     *      2. array("id"=>"1","name"=>"sky")
     *      3. 允许对象如new User(id="1",name="green");
     */
    public static function get($xmlObject_classname, $filter = null)
    {
        if ($xmlObject_classname == null) {
            $classname = get_called_class();
        } else {
            $classname = $xmlObject_classname;
        }
        $filename     = call_user_func("$classname::address");
        $spec_library = UtilXmlSimple::fileXmlToArray($filename);
        $result       = array();
        $classname    = lcfirst($classname);
        foreach ($spec_library[$classname] as $block) {
            $blockAttr = $block[Util::XML_ELEMENT_ATTRIBUTES];
            $result[]  = $blockAttr;
        }
        return $result;
    }

    /**
     * Xml数据对象总计数
     *
     * @param object|string|array $filter
     *      $filter 格式示例如下:
     *
     *          0. 允许对象如new User(id="1",name="green");
     *          1. "id=1","name='sky'"
     *          2. array("id=1","name='sky'")
     *          3. array("id"=>"1","name"=>"sky")
     * @return 对象总计数
     */
    public static function count()
    {
        $result       = 0;
        $classname    = get_called_class();
        $filename     = call_user_func("$classname::address");
        $spec_library = UtilXmlSimple::fileXmlToArray($filename);
        if (($spec_library != null ) && (count($spec_library) ) > 0) {
            foreach ($spec_library as $dataobjets) {
                $result = count($dataobjets);
            }
        }
        return $result;
    }

    /**
     * Xml对象分页
     * @param string $xmlObject_classname 具体的Xml对象类名
     * @param int $startPoint  分页开始记录数
     * @param int $endPoint    分页结束记录数
     * @param string|array $filter 过滤条件
     *
     * 示例如下:
     *
     *      string[只有一个查询条件]
     *
     *      1. id="1"--精确查找
     *      2. name contain 'sky'--模糊查找
     *
     *      array[多个查询条件]
     *
     *      1. array("id"=>"1","name"=>"sky")--精确查找
     *      2. array("id"=>"1","name contain 'sky'")--模糊查找
     * @return mixed 对象分页
     */
    public static function queryPage($startPoint, $endPoint, $filter = null, $xmlObject_classname = null)
    {
        if ($xmlObject_classname == null) {
            $classname = get_called_class();
        } else {
            $classname = $xmlObject_classname;
        }
        $filename = call_user_func("$classname::address");
        $spec_library = UtilXmlSimple::fileXmlToArray($filename);
        $result = array();
        $classname = lcfirst($classname);
        foreach ($spec_library[$classname] as $block) {
            $blockAttr = $block[Util::XML_ELEMENT_ATTRIBUTES];
            if (self::isValidData($blockAttr, $filter)) {
                $result[] = $blockAttr;
            }
        }
        $result = array_slice($result, $startPoint, $endPoint);
        return $result;
    }

    /**
     * 查看是否过滤条件允许的数据。
     * @param array $blockAttr
     * @param $filter filter 过滤条件
     * 示例如下:
     *
     *      string[只有一个查询条件]
     *
     *      1. id="1"--精确查找
     *      2. name contain 'sky'--模糊查找
     *
     *      array[多个查询条件]
     *
     *      1. array("id"=>"1","name"=>"sky")--精确查找
     *      2. array("id"=>"1","name contain 'sky'")--模糊查找
     */
    public static function isValidData($blockAttr, $filter)
    {
        if (empty($filter)) {
            return true;
        }
        if (is_string($filter)) {
            if (contain($filter, "and")) {
                $condition = explode("and", $filter);
                if (count($condition) == 2) {
                    $column    = trim($condition[0]);
                    $col_value = trim($condition[1]);
                    if (array_key_exists($column, $blockAttr)) {
                        $block_value = $blockAttr[$column];
                        if (strtolower($block_value) == strtolower($col_value)) {
                            return true;
                        }
                    }
                }
            } elseif (contain($filter, "contain")) {
                $condition = explode("contain", $filter);
                if (count($condition) == 2) {
                    $column    = trim($condition[0]);
                    $col_value = trim($condition[1]);
                    if (array_key_exists($column, $blockAttr)) {
                        $block_value = $blockAttr[$column];
                        if (contain($block_value, $col_value)) {
                            return true;
                        }
                    }
                }
            }
        } else {
            foreach ($filter as $key => $value) {
                if (is_int($key)) {
                    $condition = explode("contain", $value);
                    if (count($condition) == 2) {
                        $column    = trim($condition[0]);
                        $col_value = trim($condition[1]);
                        if (array_key_exists($column, $blockAttr)) {
                            $block_value = $blockAttr[$column];
                            $col_value   = str_replace("'", "", $col_value);
                            if (contain($block_value, $col_value)) {
                                return true;
                            }
                        }
                    }
                } else {
                    if (array_key_exists($key, $blockAttr)) {
                        $block_value = $blockAttr[$key];
                        if (strtolower($block_value) == strtolower($value)) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }


    /**
     * 保存Xml对象的信息
     */
    public function save()
    {
        $this->commitTime = UtilDateTime::now();
        $data = UtilObject::object_to_array($this);
        $classname = $this->classname();
        $filename  = call_user_func("$classname::address");
        $xml       = UtilXmlSimple::fileXmlToObject($filename);
        $classname = lcfirst($classname);
        $child     = $xml->addChild($classname);//取该对象的类名作为节点名，头字母转化为小写
        $this->id  = microtime(false) . "";
        $this->id  = str_replace(" ", "", $this->id);
        $this->id  = str_replace(".", "", $this->id);
        $child->addAttribute(self::$name_id_property, $this->id);
        foreach ($data as $key => $value) {
            if ($value != null && !endWith($key, "Show")) {
                $value = htmlentities($value, ENT_COMPAT, "UTF-8");
                $child->addAttribute($key, $value);
            }
        }
        $dom = new DOMDocument('1.0', "UTF-8");
        $dom->preserveWhiteSpace = false;
        $dom->formatOutput = true;
        $dom->loadXML($xml->asXML());
        $dom->save($filename);
        //$xml->asXML($filename);
        return $this;
    }

    /**
     * 更新Xml对象的信息
     */
    public function update()
    {
        $this->updateTime = UtilDateTime::now();
        $data = UtilObject::object_to_array($this);
        unset($data[self::$name_id_property]);
        $node      = $this->getId();
        $classname = $this->classname();
        $filename  = call_user_func("$classname::address");
        $xml       = UtilXmlSimple::fileXmlToObject($filename);
        $classname = lcfirst($classname);
        $xml_child = $xml->xpath("//$classname" . "[@" . self::$name_id_property . "='$node']");
        if ($xml_child) {
            $xml_attributes = $xml_child[0];
            if ($xml_child) {
                $attributes = $xml_attributes->attributes();
                if ($attributes) {
                    $arrObjData = get_object_vars($attributes);
                    $arrObjData = end($arrObjData);
                    foreach ($arrObjData as $key => $value) {
                        $methodName = "set" . ucfirst($key);
                        if (method_exists($this, $methodName)) {
                            $this->$methodName($value);
                        }
                    }
                }
            }
        }
        foreach ($data as $key => $value) {
            if (property_exists($attributes, $key)) {
                $attributes->$key = $value;
            } else {
                if ($value != null && !endWith($key, "Show")) {
                    if ($attributes) {
                        $attributes->addAttribute($key, $value);
                    }
                }
            }
            $this->$key = $value;
        }
        $xml->asXML($filename);
        return $this;
    }

    /**
     * 删除Xml对象的信息
     */
    public function delete()
    {
        $node      = $this->getId();
        $classname = $this->classname();
        $filename  = call_user_func("$classname::address");
        $xml       = UtilXmlSimple::fileXmlToObject($filename);
        $classname = lcfirst($classname);
        $xml_child = $xml->xpath("//$classname" . "[@" . self::$name_id_property . "='$node']");
        foreach ($xml_child as $el) {
            if ($el[self::$name_id_property] == $node) {
                $domRef = dom_import_simplexml($el);
                $domRef->parentNode->removeChild($domRef);
            }
        }
        $dom = new DOMDocument('1.0', "UTF-8");
        $dom->preserveWhiteSpace = false;
        $dom->formatOutput = true;
        $dom->loadXML($xml->asXML());
        $dom->save($filename);
        return true;
    }

    /**
     * 将XML对象转换成Array数组
     * @param $isAll 是否对象所有的field都要生成，包括没有内容或者内容为空的field
     * @return 数组
     */
    public function toArray($isAll = true)
    {
        return UtilObject::object_to_array($this, $isAll);
    }
}
